/***********************************************************************************************************************
*  OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
*  See also https://openstudio.net/license
***********************************************************************************************************************/

#ifndef MODEL_MODELMERGER_HPP
#define MODEL_MODELMERGER_HPP

#include "ModelAPI.hpp"
#include "Model.hpp"

#include "../utilities/core/Logger.hpp"
#include "../utilities/core/StringStreamLogSink.hpp"

#include <map>

namespace openstudio {
namespace model {
  class Site;
  class Facility;
  class Building;
  class Space;
  class ShadingSurfaceGroup;
  class ThermalZone;
  class SpaceType;
  class BuildingStory;
  class BuildingUnit;
  class DefaultConstructionSet;

  /** ModelMerger updates content in a current OpenStudio Model based on data from a new OpenStudio Model.
    *   A map of handles which relates objects in the current OpenStudio Model to objects in the new OpenStudio Model is required.
    *   Objects in current OpenStudio Model may map to 0-1 objects in new OpenStudio Model.  If an object in the new OpenStudio Model
    *   does not correspond to an object in the current OpenStudio Model it is assumed to be a new object.
    */
  class MODEL_API ModelMerger
  {
   public:
    ModelMerger();

    /// Merges changes from newModel into currentModel
    /// Handle mapping is mapping of handles in currentModel (keys) to handles in newModel (values)
    void mergeModels(Model& currentModel, const Model& newModel, const std::map<UUID, UUID>& handleMapping);

    /// List of IddObjectTypes which are merged
    std::vector<IddObjectType> iddObjectTypesToMerge() const;

    /// Allow users to specify which IddObjectTypes to merge, not yet implemented (always returns false)
    bool setIddObjectTypesToMerge(const std::vector<IddObjectType>& iddObjectTypesToMerge);

    /// Suggest a handle mapping between currentModel and newModel
    /// First checks if objects with same handle and type exist
    /// Second checks if objects with same CADObjectId attribute and type exist
    /// Third checks if objects with same name and type
    std::map<UUID, UUID> suggestHandleMapping(const Model& currentModel, const Model& newModel) const;

    /// Get warning messages generated by the last translation.
    std::vector<LogMessage> warnings() const;

    /// Get error messages generated by the last translation.
    std::vector<LogMessage> errors() const;

   private:
    REGISTER_LOGGER("openstudio.model.ModelMerger");

    void mergeSite(Site& currentSite, const Site& newSite);
    void mergeFacility(Facility& currentFacility, const Facility& newFacility);
    void mergeBuilding(Building& currentBuilding, const Building& newBuilding);
    void mergeSpace(Space& currentSpace, const Space& newSpace);
    void mergeShadingSurfaceGroup(ShadingSurfaceGroup& currentGroup, const ShadingSurfaceGroup& newGroup);
    void mergeThermalZone(ThermalZone& currentThermalZone, const ThermalZone& newThermalZone);
    void mergeSpaceType(SpaceType& currentSpaceType, const SpaceType& newSpaceType);
    void mergeBuildingStory(BuildingStory& currentBuildingStory, const BuildingStory& newBuildingStory);
    void mergeBuildingUnit(BuildingUnit& currentBuildingUnit, const BuildingUnit& newBuildingUnit);
    void mergeDefaultConstructionSet(DefaultConstructionSet& currentDefaultConstructionSet, const DefaultConstructionSet& newDefaultConstructionSet);

    boost::optional<UUID> getNewModelHandle(const UUID& currentHandle);
    boost::optional<UUID> getCurrentModelHandle(const UUID& newHandle);

    boost::optional<WorkspaceObject> getCurrentModelObject(const WorkspaceObject& newObject);

    StringStreamLogSink m_logSink;

    Model m_currentModel;
    Model m_newModel;
    std::set<UUID> m_newMergedHandles;
    std::vector<IddObjectType> m_iddObjectTypesToMerge;
    std::map<UUID, UUID> m_currentToNewHandleMapping;
    std::map<UUID, UUID> m_newToCurrentHandleMapping;
  };

}  // namespace model
}  // namespace openstudio
#endif  //MODEL_MODELMERGER_HPP
